import java.io.*;
import java.util.Vector;

/* NOTE: PLEASE FEEL FREE TO USE ALL, PART, OR NONE OF THIS FILE
   IT IS SIMPLY GIVEN TO SERVE AS A STARTING POINT */


public class CodeGen {

	public static String fp = "$fp";
	public static String sp = "$sp";
	public static String gp = "$gp";
	public static String zero = "$0";
    Boolean tempRegs[]; //there are 8 temporary registers
    Boolean args[];     //there are 4 arguments
    Boolean savedTempRegs[];//there are 8 saved temporary registers   
	Vector<String> codeSeg; //Storage for code/text segment
	Vector<String> dataSeg; //Storage for any data constants
	
	int mainInstruction; //Integer location for the first instruction of main in the codeSeg
	int nextInstruction; //Storage for next available instruction location
	int nextLocalLocation;//Storage for counting the next available local data cell
	int nextGlobalLocation;//Storage for counting the next available global data cell
	int labelCounter; //Used for generating unique labels as needed throughout the program
   
    CodeGen()
    {
      tempRegs = new Boolean[8];
      args = new Boolean[8];
      savedTempRegs = new Boolean[8];
      nextInstruction = 0;
      nextLocalLocation = 0;
      nextGlobalLocation = 0;
      mainInstruction = -1;
      codeSeg = new Vector<String>();
      dataSeg = new Vector<String>();
      labelCounter = 0;
      
      for(int i = 0; i < 8; i++)
      {
    	  args[i] = savedTempRegs[i] = tempRegs[i] = false;    	  
      }
    }

    //Returns a unique label
    public String makeLabel() {
    	String label = "Label"+labelCounter;
    	labelCounter++;
    	return label;
    }
    
    //Insert an instruction into the code segment
    public void insertInstruction(String instr)
    {	
    	codeSeg.add(instr);    	
    }
    
    //Insert an allocation command into the data segment
    public void insertData(String data)
    {
    	dataSeg.add(data);
    }
    
    //Resets the local allocation counter (i.e. when you exit a procedure)
    public void resetLocalLocation()
    {
    	nextLocalLocation = 0;
    }
    
    //Resets the global allocation counter (if you should need it)
    public void resetGlobalLocation()
    {
    	nextGlobalLocation = 0;
    }

    
    //Allocates memory by incrementing the local location counter
    //Returns the first address of the memory block allocated
    //Allocates a memory block as specified in allocationSize
    //Allocates based on the current local data segment counter
    public int getNextLocalLocation(int allocationSize)
    {
    	nextLocalLocation += allocationSize;
		return nextLocalLocation - allocationSize;
    }

    //Allocates memory by incrementing the global location counter
    //Returns the first address of the memory block allocated
    //Allocates a memory block as specified in allocationSize
    //Allocates based on the current global data segment counter
    public int getNextGlobalLocation(int allocationSize)
    {
       nextGlobalLocation += allocationSize;
       return nextGlobalLocation - allocationSize;
    }

    //Returns the text of the instruction at the position specified
    public String getInstructionAt(int position)
    {
        return codeSeg.elementAt(position);
    }
    
    //Substitutes the text of the instruction at the position specified
    //with the value stored in update string
    public void updateInstructionAt(int position, String update)
    {
    	codeSeg.set(position, update);
    }
    
    //Return the next available instruction location in the code segment
    public int getNextInstructionNumber() {
    	return codeSeg.size();
    }
    
    //Return the next available instruction location in the data segment
    public int getNextDataNumber() {
    	return dataSeg.size();
    }

    //allocate a temporary register
    public int getTemporaryRegister()
	{ for(int i = 0; i < 8; i++)
		if(!tempRegs[i])
		{
			tempRegs[i] = true;
			return i;
		}
	  return -1;
	}

    //allocate a temporary register that is saved across a call
    public int getTemporarySavedRegister()
	{ for(int i = 0; i < 8; i++)
		if(!savedTempRegs[i])
		{  
			savedTempRegs[i] = true;
			return i;
		}
	  return -1;
	}
    
    //release a temporary register indicated by regNum
	public void releaseTemporaryRegister(int regNum)
	{
		tempRegs[regNum] = false;
	}
	
    //release a saved temporary register indicated by regNum
	public void releaseTemporarySavedRegister(int regNum)
	{
		savedTempRegs[regNum] = false;
	}
	
	//deallocate all temporary registers
	public void releaseAllTemporaryRegisters()
	{
		for(int i = 0; i < tempRegs.length; i++)
			tempRegs[i] = false;
	}

	//deallocate all saved temporary registers
    public void releaseAllTemporarySavedRegisters()
	{
    	for(int i = 0; i < savedTempRegs.length; i++)
    		savedTempRegs[i] = false;
	}
	
    //indicate which instruction in the code segment is the first instruction in main
    //used to determine where/when to print out the label for main
	public void setMainInstruction(int instr) {mainInstruction = instr;}
	
	//generate code to print an integer; the value is stored in the register number
	//indicated by parameter reg
	public void insertPrintIntSequence(int reg)
	{
        codeSeg.add("move      $a0, $t"+reg+"       # load the integer");
        codeSeg.add("li      $v0,1                  # Prepare to print an integer");
        codeSeg.add("syscall                        # print it!!");
        codeSeg.add("la $a0, newline                # and then print out a newline.");
        codeSeg.add("li $v0, 4 ");
        codeSeg.add("syscall        ");
	}

	//generate code to read an integer; the value is stored into a register and the 
	//register number is the return result of the function
	public int insertInputIntSequence()
	{
		int regNum = getTemporaryRegister();
		codeSeg.add("li      $v0,5                  # Prepare to read an integer");
		codeSeg.add("syscall                        # print it!!");
        codeSeg.add("li 	 $t"+getTemporaryRegister()+", $v0 ");
        codeSeg.add("syscall        ");
		return regNum;
	}

	//generate code to exit the program
	public void insertExitSequence()
	{
           /* FILL THIS IN */
		codeSeg.add("li      $v0,10                  # Prepare to exit program");
        codeSeg.add("syscall                        # exit");
	}
	
	//Print the program to the provided stream
	public void dumpProgram(PrintStream s)
	{
		s.println(".data                         # BEGIN Data Segment");
		s.println("newline:      .asciiz       \"\\n\"");
	
		for(int i = 0; i < dataSeg.size(); i++)
		{ s.println(dataSeg.elementAt(i));}

		s.println("                              # END Data Segment");
		
		
		s.println(".text                         # BEGIN Code Segment");
		for(int i = 0; i < codeSeg.size(); i++)
		{ 
			if(i == mainInstruction)
				s.print("main:     ");
			s.println(codeSeg.elementAt(i));
		}
		    s.println("                              # END Code Segment");
	}
}
